<template>
	<view class="ff-page-refresh">
		<view v-if="showLoadProgressBar && $ff.util.inArray(pullRefreshStatus, ['loading', 'complete'])"
			:style="{width: loadProgressBarStatus+'%', backgroundColor: loadProgressBarColor}"
			class="load-progress-bar">
		</view>
		<view class="ff-page-refresh-wrapper" :style="{
					transform: 'translateY('+ pullRefreshDeviation +'px)',
					transition: pullRefreshTransition + 's',
				}" @touchstart="pageTouchstart" @touchmove="pageTouchmove" @touchend="pageTouchend" @touchcancel="pageTouchcancel">
			<!-- 下拉刷新 -->
			<view class="ff-pull-refresh" :style="{height: pullRefreshWrapperHeight + 'rpx'}">
				<slot name="pull-refresh" :status="pullRefreshStatus">
					<slot v-if="pullRefreshStatus == 'prepare'" name="pull-refresh-prepare">
						<image :style="{
							transform: 'rotate('+ pullRefresh.moveY * 3 +'deg)',
						}" class="ff-pull-refresh-icon" :src="prepareIcon"></image>
					</slot>
					<slot v-if="pullRefreshStatus == 'ready'" name="pull-refresh-ready">
						<image :style="{
							transform: 'rotate('+ pullRefresh.moveY * 3 +'deg)',
						}" class="ff-pull-refresh-icon" :src="readyIcon"></image>
					</slot>
					<slot v-if="pullRefreshStatus == 'loading'" name="pull-refresh-load +ing">
						<image class="ff-pull-refresh-icon ready active" :src="loadingIcon"></image>
					</slot>
					<slot v-if="pullRefreshStatus == 'cancel'" name="pull-refresh-complete">
						<image class="ff-pull-refresh-icon" :src="cancelIcon"></image>
					</slot>
					<slot v-if="pullRefreshStatus == 'complete'" name="pull-refresh-complete">
						<image class="ff-pull-refresh-icon" :src="completeIcon"></image>
					</slot>
					<slot v-if="pullRefreshStatus == 'error'" name="pull-refresh-error">
						<view class="ff-pull-refresh-error">
							{{pullRefreshData.error}}
						</view>
					</slot>
				</slot>
			</view>
			<scroll-view class="ff-page-refresh-scroll-view" :scroll-y="enableScroll" :show-scrollbar="enableScroll">
				<view class="ff-page-refresh-children" :pageRefresh="this" ref="pageRefreshChildren">
					<slot></slot>
				</view>
				<!-- 上滑加载 -->
				<view class="ff-push-load" v-show="showpushLoad">
					<view class="ff-push-load-wrapper">
						<slot name="push-load" :status="pushLoadStatus">
							<slot v-if="pushLoadStatus == 'loading'" name="push-load-loading">
								<view class="ff-push-load-icon-wrapper" @click="pagePushLoadCancel()">
									<image class="ff-push-load-icon ready active" :src="loadingIcon"></image>
									<view>加载中</view>
								</view>
							</slot>
							<slot v-if="pushLoadStatus == 'cancel'" name="push-load-complete">
								<view class="ff-push-load-icon-wrapper" @click="pagePushLoadLoading()">
									<image class="ff-push-load-icon" :src="cancelIcon"></image>
									<view>取消加载</view>
								</view>
							</slot>
							<slot v-if="pushLoadStatus == 'complete'" name="push-load-complete">
								<view class="ff-push-load-icon-wrapper">
									<image class="ff-push-load-icon" :src="completeIcon"></image>
									<view>加载完成</view>
								</view>
							</slot>
							<slot v-if="pushLoadStatus == 'finish'" name="push-load-finish">
								<view class="ff-push-load-icon-wrapper">
									<view>没有更多数据了</view>
								</view>
							</slot>
							<slot v-if="pushLoadStatus == 'error'" name="push-load-error">
								<view class="ff-push-load-icon-wrapper" @click="pagePushLoadLoading()">
									<image class="ff-push-load-icon" :src="cancelIcon"></image>
									<view class="ff-push-load-error">
										{{pushLoadData.error}}
									</view>
								</view>
							</slot>
						</slot>
					</view>
				</view>
			</scroll-view>
		</view>
	</view>
</template>
<script>
	export default {
		name: "ff-page-refresh",
		options: {
			virtualHost: true
		},
		props: {
			page: {
				type: Number,
			},
			total: {
				type: Number,
			},
			data: {
				type: Object,
			},
			list: {
				type: Array,
			},
			// 下拉刷新的高度 rpx
			pullRefreshHeight: {
				type: Number,
				default: 80
			},
			prepareIcon: {
				type: String,
				default: ""
			},
			cancelIcon: {
				type: String,
				default: "",
			},
			readyIcon: {
				type: String,
				default: ""
			},
			loadingIcon: {
				type: String,
				default: ""
			},
			completeIcon: {
				type: String,
				default: "",
			},
			completeTimeOut: {
				type: Number,
				default: 500,
			},
			cancelTimeOut: {
				type: Number,
				default: 500,
			},
			// 是否显示进度条
			showLoadProgressBar: {
				type: Boolean,
				default: true,
			},
			
			// 是否显示下拉刷新
			showPullRefresh: {
				type: Boolean,
				default: true,
			},
			
			// 是否显示上滑加载
			showpushLoad: {
				type: Boolean,
				default: true,
			},
			
			// 进度条颜色
			loadProgressBarColor: {
				type: String,
				default: "#e7e7e7",
			},
		},
		data() {
			return {
				// 动态加载编号
				uuid: null,

				// 下拉刷新
				pullRefresh: {
					startY: null,
					moveY: null,
				},

				pullRefreshData: {
					error: null,
				},

				// 上滑加载
				pushLoad: {

				},
							
				pushLoadData: {
					error: null,
				},
				
				// 是否显示下拉刷新
				isShowPullRefresh: this.showPullRefresh,
				
				// 是否显示上滑加载
				isShowpushLoad: this.showpushLoad,

				// 传递消息
				error: null,

				// 顶部加载进度条
				loadProgressBarStatus: 0,
				loadProgressBarInterval: null,

				// 下拉刷新状态
				pullRefreshStatus: 'none', // none 无状态 prepare 准备 ready 就绪 loading 刷新中 complete 已完成 error 失败 cancel 取消 pending 挂起
				// 上滑加载状态
				pushLoadStatus: 'none', // none 无状态 loading 刷新中 complete 已完成 finish 结束 error 失败 cancel 取消 pending 挂起

				pushLoadObserver: null,
				pushLoadElementShow: false,
				pushLoadInterval: null,
				
				pageChildren: null,
			};
		},
		watch: {
			pullRefreshStatus(status) {
				this.$emit("pullRefreshStatus", {status: status});
				if (this.pageChildren.pullRefreshChange != undefined) {
					this.pageChildren.pullRefreshChange({ status: status });
				}
				if (status == 'loading') {
					if (this.uuid == null) {
						this.uuid = this.$ff.util.guid();
					}
					this.pushLoadStatus = "pending";
					this.pullRefresh.moveY = uni.upx2px(this.pullRefreshHeight);
					this.loadProgressBarStart();
				} else if (status == 'error') {
					this.pullRefresh.moveY = uni.upx2px(this.pullRefreshHeight) / 1.5;
					this.loadProgressBarCancel();
				} else if (status == 'cancel') {
					this.pullRefresh.moveY = 0;
					if (this.pushLoadStatus != 'loading') {
						this.uuid = null;
					}
					setTimeout(() => {
						this.pullRefreshStatus = "none";
					}, this.cancelTimeOut)
					this.loadProgressBarCancel();
				} else if (status == 'complete') {
					this.loadProgressBarStop();
					setTimeout(() => {
						this.pullRefreshStatus = "none";
					}, this.completeTimeOut)
				} else if (status == 'none') {
					if (this.pushLoadStatus != 'loading') {
						this.uuid = null;
					}
					this.pullRefresh.moveY = 0;
				}
			},
			pushLoadStatus(status) {
				this.$emit("pushLoadChange", {status: status});
				if (this.pageChildren.pushLoadChange != undefined) {
					this.pageChildren.pushLoadChange({ status: status });
				}
				if (status == 'loading') {
					if (this.uuid == null) {
						this.uuid = this.$ff.util.guid();
					}
				} else if (status == 'none') {
					if (this.pullRefreshStatus != 'loading') {
						this.uuid = null;
					}
				} else if (status == 'cancel') {
					if (this.pullRefreshStatus != 'loading') {
						this.uuid = null;
					}
				}
			},
		},
		computed: {
			pullRefreshDeviation() {
				if (this.isShowPullRefresh || this.pullRefreshStatus == 'error') {
					let moveY = 0;
					if (this.pullRefresh.moveY > 0) {
						moveY = this.pullRefresh.moveY;
					}
					return moveY;
				} else {
					return 0;
				}
			},
			pullRefreshWrapperHeight() {
				if (this.pullRefreshStatus == 'loading') {
					return this.pullRefreshHeight;
				} else if (this.pullRefreshStatus == 'error') {
					return this.pullRefreshHeight / 1.2;
				} else {
					return this.pullRefreshHeight;
				}
			},
			pullRefreshTransition() {
				switch (this.pullRefreshStatus) {
					case 'none':
						return 0.1;
					case 'prepare':
						return 0.1;
					case 'ready':
						return 0.1;
					case 'loading':
						return 0.5;
					case 'complete':
						return 0.5;
					case 'error':
						return 0.5;
					case 'cancel':
						return 0.5;
					default:
						return 0.5;
				}
			},
			enableScroll() {
				if (this.$ff.util.inArray(this.pullRefreshStatus, ['loading', 'complete'])) {
					return false;
				}
				return true;
			}
		},
		mounted() {
			this.initChildren();
			this.initPushLoad();
			if (this.isShowpushLoad == false) {
				this.pullRefreshStatus = 'loading';
				this.$emit("pullRefreshLoadingChange", this.pullRefreshStatus);
				if (this.pageChildren.pullRefreshLoadingChange != undefined) {
					this.pageChildren.pullRefreshLoadingChange(this.pullRefreshStatus);
				}
			}
		},
		destroyed() {
			this.closePushLoad();
		},
		methods: {
			initChildren() {
				if (this.$refs.pageRefreshChildren.$children.length != 1) {
					throw "Dropdown refresh components should be used in conjunction with subcomponents, and only one component can exist";
				}
				this.pageChildren = this.$refs.pageRefreshChildren.$children[0];
				this.pageChildren.pageRefresh = this;
			},
			initPushLoad() {
				this.pushLoadObserver = uni.createIntersectionObserver(this);
				this.pushLoadObserver.relativeTo(".ff-page-refresh-scroll-view", {
					top: 50
				}).observe('.ff-push-load', (res) => {
					if (res.intersectionRatio > 0 && !this.pushLoadElementShow) {
						this.pushLoadElementShow = true;
					} else if (!res.intersectionRatio > 0 && this.pushLoadElementShow) {
						this.pushLoadElementShow = false;
					}
				})
				this.pushLoadInterval = setInterval(() => {
					if (this.pushLoadElementShow) {
						if (this.$ff.util.inArray(this.pushLoadStatus, ['loading', 'error', 'cancel'])) {
							return;
						}
						if (this.$ff.util.inArray(this.pullRefreshStatus, ['loading', 'error', 'prepare',
								'ready', ])) {
							return;
						}
						if (this.isShowpushLoad == false) {
							this.pushLoadStatus = 'none';
							return;
						}
						this.pushLoadStatus = 'loading';
						this.$emit("pushLoadLoadingChange", this.pushLoadStatus);
						if (this.pageChildren.pushLoadLoadingChange != undefined) {
							this.pageChildren.pushLoadLoadingChange(this.pushLoadStatus);
						}
					}
				}, 200);
			},
			closePushLoad() {
				if (this.pushLoadObserver != null) {
					this.pushLoadObserver.disconnect();
				}
				if (this.pushLoadInterval != null) {
					clearInterval(this.pushLoadInterval);
				}
			},
			pageTouchstart(e) {
				if (this.isShowpushLoad == false) {
					return;
				}
				if (this.pullRefreshStatus != 'loading') {
					this.pullRefreshStatus = "prepare";
				}
				this.pullRefresh.startY = e.touches[0].pageY;
			},
			pageTouchmove(e) {
				if (this.isShowpushLoad == false) {
					return;
				}
				this.pullRefresh.moveY = (e.touches[0].pageY - this.pullRefresh.startY) * 0.4;
				if (this.pullRefresh.moveY >= uni.upx2px(this.pullRefreshHeight)) {
					this.pullRefreshStatus = 'ready';
				} else if (this.pullRefresh.moveY < uni.upx2px(this.pullRefreshHeight) && this.pullRefreshStatus ==
					'loading') {
					this.pullRefreshStatus = 'cancel';
				} else if (this.pullRefresh.moveY < uni.upx2px(this.pullRefreshHeight) && this.pullRefreshStatus ==
					'ready') {
					this.pullRefreshStatus = 'prepare';
				}
			},
			pageTouchend(e) {
				if (this.isShowPullRefresh == false) {
					return;
				}
				if (this.pullRefreshStatus == 'ready') {
					this.pullRefreshStatus = 'loading';
					this.$emit("pullRefreshLoadingChange", this.pullRefreshStatus);
					if (this.pageChildren.pullRefreshLoadingChange != undefined) {
						this.pageChildren.pullRefreshLoadingChange(this.pullRefreshStatus);
					}
				} else if (this.pullRefreshStatus == 'prepare') {
					this.pullRefreshStatus = 'none';
				}
			},
			pageTouchcancel(e){
				this.pullRefreshStatus = 'none';
			},
			loadProgressBarStart() {
				this.loadProgressBarStatus = 0;
				this.loadProgressBarInterval = setInterval(() => {
					if (this.loadProgressBarStatus <= 50) {
						this.loadProgressBarStatus += 10;
					} else if (this.loadProgressBarStatus <= 60) {
						this.loadProgressBarStatus += 8;
					} else if (this.loadProgressBarStatus <= 70) {
						this.loadProgressBarStatus += 6;
					} else if (this.loadProgressBarStatus <= 80) {
						this.loadProgressBarStatus += 4;
					} else if (this.loadProgressBarStatus <= 90) {
						this.loadProgressBarStatus += 0.5;
					} else if (this.loadProgressBarStatus <= 93) {
						this.loadProgressBarStatus += 0.3;
					} else if (this.loadProgressBarStatus <= 95) {
						this.loadProgressBarStatus += 0.1;
					}
				}, 200);
			},
			loadProgressBarCancel() {
				this.loadProgressBarStatus = 0;
				clearInterval(this.loadProgressBarInterval);
			},
			loadProgressBarStop() {
				this.loadProgressBarStatus = 100;
				clearInterval(this.loadProgressBarInterval);
			},


			pullRefreshLoading(uuid = null) {
				if (uuid != null) {
					this.uuid = uuid;
				}
				this.pullRefreshStatus = "loading";
			},
			pullRefreshComplete() {
				this.pullRefreshStatus = "complete";
			},
			pullRefreshCancel() {
				this.pullRefreshStatus = "cancel";
			},
			pullRefreshError(data) {
				this.pullRefreshStatus = "error";
				this.pullRefreshData.error = data;
			},

			pagePushLoadCancel(){
				this.pushLoadStatus = "cancel";
			},
			pagePushLoadLoading(){
				this.pushLoadStatus = "none";
			},
			pushLoadLoading(uuid = null) {
				if (uuid != null) {
					this.uuid = uuid;
				}
				this.pushLoadStatus = "loading";
			},
			pushLoadComplete() {
				this.pushLoadStatus = "complete";
			},
			pushLoadFinish() {
				this.pushLoadStatus = "finish";
			},
			pushLoadError(data) {
				this.pushLoadStatus = "error";
				this.pushLoadData.error = data;
			},
			pushLoadCancel() {
				this.pushLoadStatus = "cancel";
			},
		}
	}
</script>
<style>
	/* 下拉刷新 */

	.ff-pull-refresh {
		position: absolute;
		left: 0;
		top: 0;
		transform: translateY(-100%);
		display: flex;
		justify-content: center;
		align-items: center;
		width: 100%;
	}

	.ff-page-refresh-wrapper {
		display: flex;
		flex-direction: column;
		position: relative;
		width: 100%;
		height: 100%;
	}

	.ff-pull-refresh-icon {
		width: 60rpx;
		height: 60rpx;
		transition: .3s;
	}

	.ff-pull-refresh-icon.ready {
		transform: scaleX(1.3);
	}

	.ff-pull-refresh-icon.active {
		animation: loading 1.2s cubic-bezier(.37, 1.08, .7, .74) infinite;
	}

	.ff-pull-refresh-error {
		background-color: #ff8c8c;
		width: 100%;
		height: 100%;
		display: flex;
		align-content: center;
		justify-content: center;
		align-items: center;
	}

	/* 上拉加载 */

	.ff-push-load {
		display: flex;
		flex-direction: row;
		justify-content: center;
		align-items: center;
		height: 90rpx;
		padding-top: 10rpx;
	}

	.ff-push-load-icon-wrapper {
		display: flex;
		align-items: center;
		justify-content: center;
		align-content: center;
		flex-direction: row;
		flex-wrap: nowrap;

		font-size: 14px;
		color: #939393;
	}

	.ff-push-load-icon {
		display: block;
		width: 36rpx;
		height: 36rpx;
		transition: .3s;
		margin-right: 12rpx;
	}

	.ff-push-load-icon.ready {
		transform: scaleX(1.3);
	}

	.ff-push-load-icon.active {
		animation: loading 1.2s cubic-bezier(.37, 1.08, .7, .74) infinite;
	}

	.ff-push-load-error {
		width: 100%;
		height: 100%;
		display: flex;
		align-content: center;
		justify-content: center;
		align-items: center;
	}

	/* 其他 */

	.load-progress-bar {
		height: 2px;
		width: 0px;
		transition: all 0.3s;
	}

	.ff-page-refresh-scroll-view {
		width: inherit;
		height: inherit;
	}

	.ff-page-refresh-children {
		width: auto;
		height: auto;
	}

	@keyframes loading {
		0% {
			transform: rotate(0deg);
		}

		100% {
			transform: rotate(360deg);
		}
	}
</style>